package com.zms.maze;

import java.awt.Point;
import java.util.ArrayList;
import java.util.Dictionary;
import java.util.Hashtable;
import java.util.List;
import java.util.Stack;
import java.util.Vector;

import com.zms.maze.MummyConst.Direction;

public class PositionManager {

	//delcare the runtime object
	private Explorer explorer;
	private Mummy mummy;
	private int rule;
	private Maze maze;
	
	public PositionManager(Maze maze)
	{
		this.maze=maze;
	}
	
	public PositionManager() {
		// TODO Auto-generated constructor stub
	}
	
	//Role: find the path from the opponent to explorer base on opponent's behavior.
	//Input:
	//		mummy: the character who want to catch the explorer. I.e: mummy, scorpion  
	//		explorer: the explorer
	//		rule: the behavior of opponent
	//		key: the key which mapped the opponent to hashtable
	//		isUpdateMaze: update maze or not
	//Output:
	//		the paths from the opponent to the explorer 
	public List<Point> findPathToExplorer(Character mummy, Character explorer, int rule,int key,boolean isUpdateMaze,Maze maze)
	{
		if (maze==null)
			maze=this.maze;
		List<Point> paths=new ArrayList<Point>() ;
		//Implement in case of mummy's rule is vertical first
		int  STEPS=((rule & MummyConst.ONESTEP)!=0)?1:2;
		int jumped=0;
		boolean canMove=true;//the explorer can do next move
		int vision=getVision(mummy,explorer);
		paths.add(mummy.getPosition());
		while (vision!=0 && canMove && jumped<STEPS)
		{	
			while(vision==1 && jumped<STEPS && (maze.canMove(mummy,Direction.RIGHT) || maze.canMove(mummy,Direction.DOWN)))
			{
				if (maze.canMove(mummy,Direction.RIGHT))
					mummy.moveRight();
				else if (maze.canMove(mummy,Direction.DOWN))
					mummy.moveDown();
				vision=getVision(mummy,explorer);
				paths.add(mummy.getPosition());
				jumped+=1;
			}
			while(vision==2 && jumped<STEPS && maze.canMove(mummy,Direction.DOWN))
			{
				mummy.moveDown();
				vision=getVision(mummy, explorer);
				paths.add(mummy.getPosition());
				jumped+=1;
			}
			while(vision==3 && jumped<STEPS && (maze.canMove(mummy,Direction.LEFT) || maze.canMove(mummy,Direction.DOWN)))
			{
				if (maze.canMove(mummy,Direction.LEFT))
					mummy.moveLeft();
				else if (maze.canMove(mummy,Direction.DOWN))
					mummy.moveDown();
				vision=getVision(mummy,explorer);
				paths.add(mummy.getPosition());
				jumped+=1;
			}
			while(vision==4 && jumped<STEPS && maze.canMove(mummy,Direction.LEFT))
			{
				mummy.moveLeft();
				vision=getVision(mummy, explorer);
				paths.add(mummy.getPosition());
				jumped+=1;
			}
			while(vision==5 && jumped<STEPS && (maze.canMove(mummy,Direction.LEFT) || maze.canMove(mummy,Direction.UP)))
			{
				if (maze.canMove(mummy,Direction.LEFT))
					mummy.moveLeft();
				else if (maze.canMove(mummy,Direction.UP))
					mummy.moveUp();
				vision=getVision(mummy,explorer);
				paths.add(mummy.getPosition());
				jumped+=1;
			}
			while(vision==6 && jumped<STEPS && maze.canMove(mummy,Direction.UP))
			{
				mummy.moveUp();
				vision=getVision(mummy, explorer);
				paths.add(mummy.getPosition());
				jumped+=1;
			}
			while(vision==7 && jumped<STEPS && (maze.canMove(mummy,Direction.RIGHT) || maze.canMove(mummy,Direction.UP)))
			{
				if (maze.canMove(mummy,Direction.RIGHT))
					mummy.moveRight(); 
				else if (maze.canMove(mummy,Direction.UP))
					mummy.moveUp();
				vision=getVision(mummy,explorer);
				paths.add(mummy.getPosition());
				jumped+=1;
			}
			while(vision==8 && jumped<STEPS && maze.canMove(mummy,Direction.RIGHT))
			{
				mummy.moveRight();
				vision=getVision(mummy, explorer);
				paths.add(mummy.getPosition());
				jumped+=1;
			}
			
			canMove=maze.checkOpponentCanMoveIfExplorerWait(mummy, explorer);
		}
		if (isUpdateMaze==true)
			updateMaze(mummy,key,maze);
		
		return paths;
	}
	
	
	//Update the current position of the mummies and Scorpion
	private Maze updateMaze(Character character,int key,Maze maze) {
		//not implemented yet
		if (maze==null)
		{
			maze=this.maze;
		}
		if(character.getClass().getName()=="com.zms.maze.Mummy")
		{
			
			maze.mummiesHash.remove(key);
			maze.mummiesHash.put(key, (Mummy)character);
			
		}
		else if(character.getClass().getName()=="com.zms.maze.Scorpion")
		{
			maze.scorpionsHash.remove(key);
			maze.scorpionsHash.put(key, (Scorpion)character);
		}
		return maze;
	}
	
	
	//Update the maze:
	//1. State of toggles
	//2. The position of mummies
	//3. The position of scorpions
	
	public Maze updateMaze()
	{
		updateMummies();
		updateScorpion();
		updateToggles();
		return this.maze;
	}
	public Maze updateMaze(Maze maze)
	{
		maze=updateMummies(maze);
		maze=updateScorpion(maze);
		maze=updateToggles(maze);
		return maze;
	}
	
	public Maze updateToggles()
	{
		//if the man active/deactive the key, this will affect to the toggleline
		for(int i=0;i<maze.togglesHash.size();i++)
		{
			if(maze.toggles.get(i).togglex==maze.explorer.x && maze.toggles.get(i).toggley==maze.explorer.y)
			{
				maze.toggles.get(i).swapState();
				break;
			}
		}
		return this.maze;
	}
	public Maze updateToggles(Maze maze)
	{
		//if the man active/deactive the key, this will affect to the toggleline
		for(int i=0;i<maze.togglesHash.size();i++)
		{
			if(maze.toggles.get(i).togglex==maze.explorer.x && maze.toggles.get(i).toggley==maze.explorer.y)
			{
				maze.toggles.get(i).swapState();
				break;
			}
		}
		return maze;
	}
	public Maze updateMummies()
	{
		List<Point> paths;
		for(int i=0;i<maze.mummiesHash.size();i++)
		{
			//paths=findPathToExplorer(i, maze.getExplorer(), MummyConst.TWOSTEP|MummyConst.VERTICALFIRST);
			paths=findPathToExplorer(maze.mummiesHash.get(i),maze.explorer,MummyConst.TWOSTEP|MummyConst.VERTICALFIRST,i,true,null);
			String text="";
			for(Point p:paths)
			{
				text+=String.valueOf(p.x)+","+String.valueOf(p.y)+" - ";
			}
			
			Utilities.getInstance().log("Mummy "+String.valueOf(i)+": "+ text.substring(0, text.length()-3));
		}
		return this.maze;
	}
	public Maze updateMummies(Maze maze)
	{
		List<Point> paths;
		for(int i=0;i<maze.mummiesHash.size();i++)
		{
			//paths=findPathToExplorer(i, maze.getExplorer(), MummyConst.TWOSTEP|MummyConst.VERTICALFIRST);
			paths=findPathToExplorer(maze.mummiesHash.get(i),maze.explorer,MummyConst.TWOSTEP|MummyConst.VERTICALFIRST,i,true,maze);
			String text="";
			for(Point p:paths)
			{
				text+=String.valueOf(p.x)+","+String.valueOf(p.y)+" - ";
			}
			
			//Utilities.getInstance().log("Mummy "+String.valueOf(i)+": "+ text.substring(0, text.length()-3));
		}
		return maze;
	}
	public Maze updateScorpion()
	{
		List<Point> paths;
		for(int i=0;i<maze.scorpionsHash.size();i++)
		{
			paths=findPathToExplorer(maze.scorpionsHash.get(i),maze.explorer,MummyConst.ONESTEP|MummyConst.VERTICALFIRST,i,true,null);
			String text="";
			for(Point p:paths)
			{
				text+=String.valueOf(p.x)+","+String.valueOf(p.y)+" - ";
			}
			
			Utilities.getInstance().log("Scorpion "+String.valueOf(i)+": "+ text.substring(0, text.length()-3));
		}
		return this.maze;
	}
	public Maze updateScorpion(Maze maze)
	{
		List<Point> paths;
		for(int i=0;i<maze.scorpionsHash.size();i++)
		{
			paths=findPathToExplorer(maze.scorpionsHash.get(i),maze.explorer,MummyConst.ONESTEP|MummyConst.VERTICALFIRST,i,true,maze);
			String text="";
			for(Point p:paths)
			{
				text+=String.valueOf(p.x)+","+String.valueOf(p.y)+" - ";
			}
			
			Utilities.getInstance().log("Scorpion "+String.valueOf(i)+": "+ text.substring(0, text.length()-3));
		}
		return maze;
	}
	
	
	public static int getVision(Character mummy, Character explorer) {
		if (mummy.x<explorer.x && mummy.y<explorer.y)
			return 1;
		if (mummy.x==explorer.x && mummy.y<explorer.y)
			return 2;
		if (mummy.x>explorer.x && mummy.y<explorer.y)
			return 3;
		if (mummy.x>explorer.x && mummy.y==explorer.y)
			return 4;
		if (mummy.x>explorer.x && mummy.y>explorer.y)
			return 5;
		if (mummy.x==explorer.x && mummy.y>explorer.y)
			return 6;
		if (mummy.x<explorer.x && mummy.y>explorer.y)
			return 7;
		if (mummy.x<explorer.x && mummy.y==explorer.y)
			return 8;
		//the pos of summy and explorer are same.
		return 0;
	}
	
	public static void main(String[] args) {
		// TODO Auto-generated method stub
	}

	public void loadMaze(Maze maze) {
		this.maze=maze;
	}
	
	//private Hashtable<Maze, Integer> tracker=new Hashtable<Maze, Integer>();
	private Tracker tracker=new Tracker();
	private Maze lastMaze;
	private Direction lastDirection;
	private int stateNo=0;
	private boolean isLose=false;
	private Stack<Maze> mummyStack=new Stack<Maze>(); 
	private Hashtable< Maze,Action> transactions=new Hashtable<Maze,Action>();
	private Transaction transactionBuilder=new Transaction();
	//try to record the win states immediately during building all states.
	private Hashtable<String, Integer> stateHash=new Hashtable<String, Integer>();
	public void updateLastState(Maze state, Direction dir) 
	{
		try
		{
		this.lastMaze=state.clone();
		this.mummyStack.add(state.clone());
		this.lastDirection=dir;
		}
		catch(Exception er)
		{
			er.printStackTrace();
		}
	}
	
	public Hashtable<Maze,Action> getTransactions()
	{
		return this.transactions;
	}
	public Tracker getStateTracker()
	{
		return this.tracker;
	}
	public Transaction getTransactionBuilder()
	{
		return this.transactionBuilder;
	}
	
	
	public int buildAllState(Maze state) throws CloneNotSupportedException
	{
		isLose=false;
		stateNo++;
		lastMaze=mummyStack.peek();
		String log=String.valueOf(stateNo)+" "+"["+lastMaze.explorer.getPosString()+"-"+lastMaze.mummies.get(0).getPosString()+"+"+lastDirection.toString()+"]"+"-->"+state.explorer.getPosString()+"-"+state.mummies.get(0).getPosString();
		transactions.put(state.clone(),new Action(lastMaze.clone(), lastDirection));
		Utilities.getInstance().log(log);
		if (state.isLose())
		{
			if (!transactionBuilder.contains(lastMaze, lastDirection))
			{
				transactionBuilder.put(state, lastDirection, lastMaze);
			}
			
			tracker.put(state,MummyConst.LOSE);
			Utilities.getInstance().log("LOSE");
			return MummyConst.LOSE;
			
		}
		else if (state.isWin())
		{
			if (!transactionBuilder.contains(lastMaze, lastDirection))
			{
				transactionBuilder.put(state, lastDirection, lastMaze);
			}
			tracker.put(state,MummyConst.WIN);
			Utilities.getInstance().log("WIN");
			return MummyConst.WIN;
		}
		if (state.canMove(state.explorer, Direction.WAIT) )
		{
			mummyStack.add(state.clone());
			lastDirection=Direction.WAIT;
			state.explorer.stop();
			state=updateMaze(state);
			if (!transactionBuilder.contains(mummyStack.peek(), lastDirection))
			{
				transactionBuilder.put(state,lastDirection,mummyStack.peek());
			}
			if (!tracker.containsKey(state))
			{
				tracker.put(state,MummyConst.NON_TERMINAL);
				buildAllState(state);
			}
			state=mummyStack.pop();
		}
		if (state.canMove(state.explorer, Direction.UP) )
		{
			mummyStack.add(state.clone());
			lastDirection=Direction.UP;
			state.explorer.moveUp();
			state=updateMaze(state);
			if (!transactionBuilder.contains(mummyStack.peek(), lastDirection))
			{
				transactionBuilder.put(state,lastDirection,mummyStack.peek());
			}
			if (!tracker.containsKey(state))		
			{
				tracker.put(state,MummyConst.NON_TERMINAL);
				buildAllState(state);
			}
			state=mummyStack.pop();
		}
		if (state.canMove(state.explorer, Direction.LEFT))
		{
			mummyStack.add(state.clone());
			lastDirection=Direction.LEFT;
			state.explorer.moveLeft();
			state=updateMaze(state);
			if (!transactionBuilder.contains(mummyStack.peek(), lastDirection))
			{
				transactionBuilder.put(state,lastDirection,mummyStack.peek());
			}
			if (!tracker.containsKey(state))
			{
				tracker.put(state,MummyConst.NON_TERMINAL);
				buildAllState(state);
			}
			state=mummyStack.pop();
		}
		if (state.canMove(state.explorer, Direction.RIGHT))
		{
			mummyStack.add(state.clone());
			lastDirection=Direction.RIGHT;
			state.explorer.moveRight();
			state=updateMaze(state);
			if (!transactionBuilder.contains(mummyStack.peek(), lastDirection))
			{
				transactionBuilder.put(state,lastDirection,mummyStack.peek());
			}
			if (!tracker.containsKey(state))
			{
				tracker.put(state,MummyConst.NON_TERMINAL);
				buildAllState(state);
			}
			state=mummyStack.pop();
		}
		if (state.canMove(state.explorer, Direction.DOWN))
		{
			mummyStack.add(state.clone());
			lastDirection=Direction.DOWN;
			state.explorer.moveDown();
			state=updateMaze(state);
			if (!transactionBuilder.contains(mummyStack.peek(), lastDirection))
			{
				transactionBuilder.put(state,lastDirection,mummyStack.peek());
			}
			if (!tracker.containsKey(state))
			{
				tracker.put(state,MummyConst.NON_TERMINAL);
				buildAllState(state);
			}
			state=mummyStack.pop();
		}
		return MummyConst.NON_TERMINAL;
	}
}
	